package view;

import error.GedivaException;
import filter.IFilter;
import graphs.AbstractGraph;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import model.IGedivaModel;
import view.components.ButtonPanel;
import view.components.CheckBoxPanel;
import view.components.GraphPanel;
import view.components.FileInputBoxArea;
import view.components.MenuBar;

/**
 * View for our Picassa program. Contains constructor to build full pane with
 * components and methods to show messages.
 * 
 * @author Vijay Ram
 */

public class GedivaGUI extends JFrame implements IGedivaGUI
{
    private static final long serialVersionUID = 1L;
    private GraphPanel myGraphPanel;
    private Properties myConstants;
    private Dimension mySize;
    private IGedivaModel myModel;
    private ButtonPanel myButtons;
    private GridBagLayout myLayout;
    private GridBagConstraints myConstraints;
    @SuppressWarnings("unused")
    private FileInputBoxArea myInputArea;
    private CheckBoxPanel myFilterList;
    private URL myURL;
    private AbstractGraph<Object, Object> myGraph;
    private List<IFilter<?, ?>> mySelectedFilters;
    private Map<Object, List<Object>> myOGData;
    private MenuBar myMenuBar;

    /**
     * constructs GUI needs a IGedivaModel
     * 
     * @param model
     */
    public GedivaGUI(IGedivaModel model)
    {
        myConstants = new Properties();

        // Initialize Components
        try
        {
            myConstants.load(new FileInputStream(new File(
                    "gediva/properties/guiconstants.properties")));
        }
        catch (FileNotFoundException e)
        {
            showMessage("GUI Properties File not Found");
        }
        catch (IOException e)
        {
            showMessage("GUI Properties File not Found");
        }

        // Set Constants, set model
        myModel = model;
        mySize = new Dimension(Integer.parseInt(myConstants
                .getProperty("GUI_HEIGHT")), Integer.parseInt(myConstants
                .getProperty("GUI_WIDTH")));

        // Construct parts of GUI
        buildLayout();
        myInputArea = buildInputArea();
        myGraphPanel = buildCanvas();
        myButtons = buildDisplayPanel();
        myFilterList = buildFilterPanel();
        myMenuBar = makeMenus();

        // Set final parameters
        setTitle(myConstants.getProperty("TITLE"));
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setMinimumSize(mySize);
        pack();
        setVisible(true);
    }

    /**
     * Create the menus for the program.
     */
    private MenuBar makeMenus()
    {
        MenuBar menuBar = new MenuBar(myConstants, this);
        setJMenuBar(menuBar);
        return menuBar;
    }

    /**
     * initialize the GridBagLayout
     */
    private void buildLayout()
    {
        myLayout = new GridBagLayout();
        myConstraints = new GridBagConstraints();
        this.setLayout(myLayout);
    }

    /**
     * set up the component with the file input chooser, file save button and
     * the panel visibility toggler
     * 
     * @return
     */
    private FileInputBoxArea buildInputArea()
    {
        FileInputBoxArea inputArea = new FileInputBoxArea(myConstants, this);
        myConstraints.fill = GridBagConstraints.BOTH;
        myConstraints.gridx = 0;
        myConstraints.gridy = 2;
        myLayout.setConstraints(inputArea, myConstraints);
        this.add(inputArea);
        return inputArea;
    }

    /**
     * set up the graph panel initialized with splash screen
     * 
     * @return
     */
    private GraphPanel buildCanvas()
    {
        GraphPanel graphPanel = new GraphPanel(this);
        myConstraints.fill = GridBagConstraints.BOTH;
        myConstraints.gridx = 0;
        myConstraints.gridy = 0;
        myConstraints.gridheight = 2;
        myConstraints.gridwidth = 2;
        myConstraints.weightx = 1;
        myConstraints.weighty = 1;
        myLayout.setConstraints(graphPanel, myConstraints);
        this.add(graphPanel);
        return graphPanel;
    }

    /**
     * initialize the button panel to show the available graph types
     * 
     * @return
     */
    private ButtonPanel buildDisplayPanel()
    {
        ButtonPanel buttons = new ButtonPanel(this);

        // set default
        buttons.add(myConstants.getProperty("DEFAULT_TEXT"));

        myConstraints.gridx = 2;
        myConstraints.gridy = 0;
        myConstraints.gridheight = 1;
        myConstraints.gridwidth = 1;
        myLayout.setConstraints(buttons, myConstraints);

        this.add(buttons);
        return buttons;
    }

    /**
     * initialize the filter availability panel
     * 
     * @return
     */
    private CheckBoxPanel buildFilterPanel()
    {
        CheckBoxPanel filters = new CheckBoxPanel(this);

        // set default
        filters.add(myConstants.getProperty("DEFAULT_TEXT"));

        myConstraints.gridx = 2;
        myConstraints.gridy = 1;
        myConstraints.gridheight = 1;
        myConstraints.gridwidth = 1;
        myLayout.setConstraints(filters, myConstraints);

        this.add(filters);
        return filters;
    }

    /**
     * called when process file is clicked feeds the inputted file to the model
     * to be processed, then shows user available graph types
     */
    public void processFile(URL fileURL)
    {
        myURL = fileURL;
        try
        {
            showChartTypeSelector(myModel.getDisplayTypes(fileURL));
            myFilterList.setVisible(false);
            myFilterList.removeAll();
            myFilterList.setVisible(true);
            myGraphPanel.initializePanel();
        }
        catch (GedivaException e)
        {
            showMessage(e.getMessage());
        }
    }

    /**
     * shows the available graph types for a given data file in a JButtonPanel
     * feeds the graph type to the model and then gets a list of possible
     * filters
     * 
     * @param displayTypes
     */
    private void showChartTypeSelector(List<AbstractGraph<?, ?>> displayTypes)
    {
        myButtons.removeAll();
        myButtons.setVisible(false);
        myMenuBar.clearGraphs();
        for (int i = 0; i < displayTypes.size(); i++)
        {
            myButtons.add(displayTypes.get(i));
            myMenuBar.addGraphType(displayTypes.get(i));
        }
        myButtons.setVisible(true);
        this.setVisible(true);
        myButtons.setPreferredSize(new Dimension(300, 40));
    }

    /**
     * method called when a graph type button is clicked
     * 
     * @param iGraph
     */
    @SuppressWarnings("unchecked")
    public <X, Y> void drawGraph(AbstractGraph<X, Y> iGraph)
    {
        try
        {
            myGraph = (AbstractGraph<Object, Object>) iGraph;
            showFilterOptions(myModel.getFilters(iGraph));
            mySelectedFilters = new ArrayList<IFilter<?, ?>>();
            myGraph.setPreferredSize(new Dimension(700, 500));
            myGraphPanel.setBorder(BorderFactory
                    .createLineBorder(Color.blue, 2));
            myGraphPanel.replaceGraph(iGraph);
            myGraph.setVisible(true);
            // this.pack();
            myOGData = myModel.getData(myURL, iGraph);
            iGraph.draw(myOGData, myModel.getHeaders(myURL, myGraph));
            this.pack();
        }
        catch (GedivaException e)
        {
            showMessage(e.getMessage());
        }
    }

    /**
     * shows the available filters to be added to a graph for a given graph type
     * 
     * @param filterList
     */
    private void showFilterOptions(List<IFilter<?, ?>> filterList)
    {
        myFilterList.removeAll();
        myFilterList.setVisible(false);
        myMenuBar.clearFilters();
        for (int i = 0; i < filterList.size(); i++)
        {
            myFilterList.add(filterList.get(i));
            myMenuBar.addFilter(filterList.get(i));
        }
        myFilterList.setVisible(true);
        this.setVisible(true);
    }

    /**
     * adds given filter to mySelectedFilters and formats original data for
     * display in gui
     * 
     * @param iFilter
     */
    public void applyFilter(IFilter<?, ?> iFilter)
    {
        try
        {
            List<String> prompts = iFilter.getPrompts();
            List<String> params = new ArrayList<String>();
            for (int i = 0; i < prompts.size(); i++)
            {
                params.add(getUserInput(prompts.get(i)));
            }
            iFilter.storeParameters(params);
            mySelectedFilters.add(iFilter);
            myGraphPanel.replaceGraph(myGraph);
            myGraph.draw(myModel.applyFilters(myOGData, mySelectedFilters),
                    myModel.getHeaders(myURL, myGraph));
        }
        catch (GedivaException e)
        {
            showMessage(e.getMessage());
        }
    }

    /**
     * removes the provided filter from myselectedfilters and then reformats the
     * original data to display in the centerpiece
     * 
     * @param iFilter
     */
    public void removeFilter(IFilter<?, ?> iFilter)
    {
        try
        {
            mySelectedFilters.remove(iFilter);
            myGraph.draw(myModel.applyFilters(myOGData, mySelectedFilters),
                    myModel.getHeaders(myURL, myGraph));
        }
        catch (GedivaException e)
        {
            showMessage(e.getMessage());
        }
    }

    /**
     * show/hide the side panel with graph and filter types
     * 
     * @param b
     */
    public void togglePanelVisibility()
    {
        myFilterList.setVisible(!myFilterList.isVisible());
        myButtons.setVisible(!myButtons.isVisible());
    }

    /**
     * shows the given string to the user in a pop up message
     * 
     * @param s
     */
    public void showMessage(String s)
    {
        JOptionPane.showMessageDialog(this, s, myConstants
                .getProperty("GEDIVA_INFORMATION"),
                JOptionPane.INFORMATION_MESSAGE);
    }

    /**
     * get user input for the given prompt and return the string input
     * 
     * @param prompt
     * @return
     */
    public String getUserInput(String prompt)
    {
        return JOptionPane.showInputDialog(prompt);
    }
}
